package com.wing.gateway.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.nimbusds.jose.JWSObject;
import com.wing.common.constant.SecurityConstants;
import com.wing.common.utils.JsonResult;
import com.wing.common.utils.Result;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;


/**
 * 安全拦截全局过滤器
 *
 * @author
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class SecurityGlobalFilter implements GlobalFilter, Ordered {

    private final RedisTemplate redisTemplate;


    @Value("${spring.profiles.active}")
    private String env;

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        log.info("gateway拦截器@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@START");
        log.info("gateway拦截器....{}", request.getURI().toURL().toString());
        String url = request.getURI().toURL().toString();
        Integer startIndex=url.indexOf("//")+2;
        Integer endIndex=url.indexOf("/",startIndex);
        String domain=url.substring(startIndex,endIndex);
        if (domain.contains(":")){
            domain=domain.substring(0,domain.indexOf(":"));
        }
        log.info("gateway拦截器域名....{}", domain);

        // 线上演示环境禁止修改和删除
        // String requestPath = request.getPath().toString();
        // if (env.equals("prod") && !SecurityConstants.LOGOUT_PATH.equals(requestPath)
        //         && !StrUtil.containsAny(requestPath, "app-api")
        //         // 删除方法
        //         && (HttpMethod.DELETE.toString().equals(request.getMethodValue())
        //         // 修改方法
        //         || HttpMethod.PUT.toString().equals(request.getMethodValue()))
        // ) {
        //     return handleResponse(response, JsonResult.fail(JsonResult.Code.FORBIDDEN_OPERATION));
        // }

        // 不是正确的的JWT不做解析处理
        String token = request.getHeaders().getFirst(SecurityConstants.AUTHORIZATION_KEY);
        if (StrUtil.isBlank(token) || !StrUtil.startWithIgnoreCase(token, SecurityConstants.JWT_PREFIX)) {
            request = exchange.getRequest().mutate().header("domain",domain).build();
            exchange = exchange.mutate().request(request).build();
            return chain.filter(exchange);
        }

        // 解析JWT
        token = StrUtil.replaceIgnoreCase(token, SecurityConstants.JWT_PREFIX, Strings.EMPTY);
        String payload = StrUtil.toString(JWSObject.parse(token).getPayload());
        JSONObject jsonObject = JSONUtil.parseObj(payload);

        // 获取jti以jti为key判断redis的黑名单列表是否存在，存在则拦截访问
        String jti = jsonObject.getStr(SecurityConstants.JWT_JTI);
        Boolean isBlack = redisTemplate.hasKey(SecurityConstants.TOKEN_BLACKLIST_PREFIX + jti);
        if (isBlack) {
            return handleResponse(response, JsonResult.fail(Result.Code.TOKEN_ACCESS_FORBIDDEN));
        }

        // 获取userId以userId为key判断redis的禁用名单列表是否存在，存在则拦截访问
        String userId = jsonObject.getStr(SecurityConstants.USER_ID_KEY);
        Boolean isDisabled = redisTemplate.hasKey(SecurityConstants.TOKEN_DISABLEDLIST_PREFIX + userId);
        if (isDisabled) {
            return handleResponse(response, JsonResult.fail(Result.Code.TOKEN_ACCESS_FORBIDDEN));
        }

        // 存在token且不是黑名单，request写入JWT的载体信息
        request = exchange.getRequest().mutate()
                .header(SecurityConstants.JWT_PAYLOAD_KEY, URLEncoder.encode(payload, "UTF-8")).header("domain",domain)
                .build();
        exchange = exchange.mutate().request(request).build();
        log.info("gateway拦截器@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@END");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    private Mono<Void> handleResponse(ServerHttpResponse response, JsonResult jsonResult) {
        response.getHeaders().set("Content-Type", "application/json; charset=utf-8");
        response.getHeaders().set("Access-Control-Allow-Origin", "*");
        response.getHeaders().set("Cache-Control", "no-cache");
        String body = JSONUtil.toJsonStr(jsonResult);
        DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer)).doOnError(error -> DataBufferUtils.release(buffer));
    }
}
